﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Media;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Diagnostics;
using System.IO;
#endregion Using Statements

namespace CakeRaid
{
    // This class should never be created, rather only children classes (thus abstract)
    abstract class Enemy : GameObject
    {
        #region Attributes
        protected Path path;
        protected HUD hud;
        protected int currentTile;

        protected bool hasCake;
        protected bool adjacentToCake;
        protected List<Enemy> enemies;

        protected float slowEffect;
        protected SpriteBatch sprite;
        protected Cake cakeObject;
        protected ImageObject effects;

        //moved to the base class from specifically wasp, bee, and spider to allow the attract behavior
        protected Tower focusedTower;
        protected float attackRange;

        //list of status effects, for better checking of enemy states and usage for status animations
        //slowed, shocked, healing, and attracted
        protected List<bool> status_effects;

        //includes all behaviors for all enemies, since enums cannot be overridden
        //death and despawn are separate behaviors -- despawn occurs if the enemy manages to make it to the end
        public enum BehaviorState { Spawn, FollowPath, MoveToTower, MoveFromTower, AttackTower, AttackTrap, Death, Despawn }
        protected BehaviorState behavior;
        #endregion Attributes

        #region Constructor
        public Enemy(ImageObject imageObject, ImageObject effects, int health, int damage, float speed, Path path, Cake cakeTemp, HUD hud)
            : base(imageObject, health, damage, speed)
        {
            status_effects = new List<bool> { false, false, false, false};
            sprite = imageObject.SpriteBatch;
            IsActive = false;
            this.path = path;
            this.effects = effects;
            Image.Center = Center = path.Start.Center;
            currentTile = 0;
            slowEffect = 1;
            Image.Transparency = 0;
            Image.Rotation = ImageObject.RIGHT;
            timer = new Timer(Var.GAME_SPEED);
            this.hud = hud;
            cakeObject = cakeTemp;
            hasCake = false;
            behavior = BehaviorState.Spawn;
            attackRange = Var.ENEMY_ATTACK_RANGE;
        }
        #endregion Constructor

        #region Properties
        public BehaviorState Behavior
        {
            get { return behavior; }
            set { behavior = value; }
        }

        public bool HasCake
        {
            get { return hasCake; }
            set { hasCake = value; }
        }

        public bool AdjacentToCake
        {
            get { return adjacentToCake; }
            set { adjacentToCake = value; }
        }

        public Tower FocusedTower
        {
            get { return focusedTower; }
            set { focusedTower = value; }
        }

        public Path Path
        {
            get { return path; }
            set { path = value; }
        }

        public float AttackRange
        {
            get { return attackRange; }
        }

        public List<bool> Status_effects
        {
            get { return status_effects; }
        }

        public int CurrentTile
        {
            get { return currentTile; }
        }

        /// <summary> Ranges from 0-1. One is normal speed. </summary>
        public float SlowEffect
        {
            get { return slowEffect; }
            set { slowEffect = value; if (value <= Var.ENEMY_SLOW_CAP) { slowEffect = Var.ENEMY_SLOW_CAP; } if (value >= 1) { slowEffect = 1; } }
        }

        /// <summary> Incorporates speed, slow effect, and game speed. </summary>
        public float Speed_Actual
        {
            get { return Speed * Var.GAME_SPEED * slowEffect; }
        }
        #endregion Properties

        #region Methods

        #region Update
        public void Update(GameTime gameTime, List<Enemy> e, List<Vector2> droppedCake)
        {
            time = gameTime;
            timer.Update(gameTime, Var.GAME_SPEED);

            enemies = e;
            cakeObject.droppedCake = droppedCake;

            effects.X = (int)this.X; effects.Y = (int)this.Y;

            if (IsActive)
            {
                if (behavior != BehaviorState.Death)
                    healthBar.Update(gameTime, CurrentHealth, Center.X, Y);

                if ((behavior != BehaviorState.Spawn) && (behavior != BehaviorState.Despawn) && (behavior != BehaviorState.Death))
                {
                    MoveBy(Speed_Actual); // move the enemy (properly)

                    if (path.GetTile(currentTile) == path.End)
                    {
                        behavior = BehaviorState.Despawn;
                        timer.Start(gameTime, Var.DESPAWN_TIME);
                        Despawning();
                    }
                }
                else if (behavior == BehaviorState.Despawn)
                {
                    Despawning();
                }
                else if (behavior == BehaviorState.Death)
                {
                    Dying();
                }
                else if (behavior == BehaviorState.Spawn)
                {
                    Spawning();
                }
            }
        }
        #endregion Update

        #region Pathing
        public abstract void MoveBy(float num);

        public void RefreshFocusedTower()
        {
            foreach (Tile tile in path.GetTile(currentTile).Neighbors)
            {
                if ((tile != null) && (tile.OccupiedBy is Tower))
                {
                    focusedTower = ((Tower)(tile.OccupiedBy));
                }
            }
        }
        #endregion Pathing

        #region Let Tower find where you will be
        /// <summary> Does NOT WORK on high enemy / game speeds currently. </summary>
        public float CheckWhereIWillBe(Tower tower)
        {
            int ii = 1;
            Vector2 savedPosition = Point;
            int tileNum = currentTile;
            do
            {
                if (tower.BulletSpeed * Var.GAME_SPEED * ii > Vector2.Distance(tower.Center, Center))
                {
                    // This should be enough? if not, go back to last tile, and then step-by-step (every 1-2 space) go forward, and break right after the above if is true
                    break;
                }
                else
                {
                    MoveBy(Speed_Actual);
                    ii++;
                }
            } while (ii > 0);
            float angle = (float)Math.Atan2((float)Center.Y - tower.Center.Y, (float)Center.X - tower.Center.X);
            Point = savedPosition;
            currentTile = tileNum;
            return angle;
        }
        #endregion Let Tower find where you will be

        #region Spawning / Despawning Stuff
        public void Start(GameTime gameTime)
        {
            IsActive = true;
            Image.Transparency = 0;
            Image.Rotation = ImageObject.RIGHT;

            behavior = BehaviorState.Spawn;

            timer.Start(gameTime, Var.SPAWN_TIME);
        }

        /// <summary> Called in Move. Controls enemy's behavior when spawning. </summary>
        private void Spawning()
        {
            if (!timer.Finished)
            {
                Image.Transparency = timer.Percent * 100;
                Image.Rotation = (float)(Var.SPAWN_SPINS * (Math.PI * 2) * timer.Percent);//percComplete);
            }
            else
            {
                Image.Transparency = 100;
                Image.Rotation = ImageObject.RIGHT;
                timer.End();
                behavior = BehaviorState.FollowPath;
                IsActive = true;
            }
        }

        /// <summary> Called in Move. Controls enemy's behavior when despawning. </summary>
        private void Despawning()
        {
            if (!timer.Finished)
            {
                Image.Transparency = (1 - timer.Percent) * 100;
            }
            else
            {
                Image.Transparency = 100;
                Image.Rotation = ImageObject.RIGHT;
                timer.End();

                behavior = BehaviorState.Death;
                RemoveAllStatuses();
                
                if (HasCake)
                {
                    hud.EnemyGotCake();
                }

                IsActive = false;
            }
        }

        private void Dying()
        {
            if (!timer.Finished)
            {
                Image.Transparency = timer.Percent * 100;
                Image.Rotation = (float)(Var.DEATH_SPINS * (Math.PI * 2) * timer.Percent);
                Image.Resize = new Vector2(1 - timer.Percent);
                CenterImage();
            }
            else
            {
                Image.Transparency = 100;
                Image.Rotation = ImageObject.RIGHT;
                timer.End();
                RemoveAllStatuses();
                IsActive = false;
            }
        }
        #endregion Spawning / Despawning Stuff

        #region Get Damaged (Hit)
        public void Hit(int hitDmg)
        {
            if (IsActive && (behavior != BehaviorState.Spawn) && (behavior != Enemy.BehaviorState.Death))
            {
                CurrentHealth -= hitDmg;
                healthBar.Show(time);

                if (CurrentHealth <= 0)
                {
                    behavior = BehaviorState.Death;
                    healthBar.Hide();
                    hud.EnemyDied(CalculateDeathReward());
                    timer.Start(time, Var.DYING_TIME);
                }
            }
        }

        #region Apply and Remove Statuses
        public void ApplyStatus (Var.StatusType effectName)
        {
            switch (effectName)
            {
                case Var.StatusType.Slowed:
                    status_effects[0] = true;
                    break;
                case Var.StatusType.Shocked:
                    status_effects[1] = true;
                    break;
                case Var.StatusType.Healing:
                    status_effects[2] = true;
                    break;
                case Var.StatusType.Attracted:
                    status_effects[3] = true;
                    break;
                default:
                    break;
            }
        }

        public void RemoveStatus (Var.StatusType effectName)
        {
            switch (effectName)
            {
                case Var.StatusType.Slowed:
                    status_effects[0] = false;
                    break;
                case Var.StatusType.Shocked:
                    status_effects[1] = false;
                    break;
                case Var.StatusType.Healing:
                    status_effects[2] = false;
                    break;
                case Var.StatusType.Attracted:
                    status_effects[3] = false;
                    break;
                default:
                    break;
            }
        }

        public void RemoveAllStatuses()
        {
            status_effects[0] = false;
            status_effects[1] = false;
            status_effects[2] = false;
            status_effects[3] = false;
        }

        #endregion Apply and Remove Statuses

        private int CalculateDeathReward()
        {
            return (int)((StartHealth * Speed) / 2.0f);
        }
        #endregion Get Damaged (Hit)

        #region Heal
        public void Heal (int healAmt, int maxHealth)
        {
            if (IsActive && (behavior != BehaviorState.Spawn) && (behavior != BehaviorState.Death))
            {
                if (CurrentHealth + healAmt <= maxHealth)
                {
                    ApplyStatus(Var.StatusType.Healing);
                    CurrentHealth += healAmt;
                    healthBar.Show(time);
                }
            }
        }
        #endregion Heal

        #region Slow
        public void Slow()
        {
            if (IsActive && (behavior != BehaviorState.Spawn) && (behavior != BehaviorState.Death))
            {
                ApplyStatus(Var.StatusType.Slowed);
                slowEffect *= 0.8f;
            }
        }
        #endregion Slow

        #region Shock
        public void Shock()
        {
            if (IsActive && (behavior != BehaviorState.Spawn) && (behavior != BehaviorState.Death))
            {
                ApplyStatus(Var.StatusType.Shocked);
            }
        }
        #endregion Shock

        #region Find Cake
        //Search for adjacent cake. Modified and moved from Game.cs
        public void FindCake()
        {
            Tile[] neighbors = path.GetTile(currentTile).Neighbors;
            for (int i = 0; i < neighbors.Length; i++)
            {
                if (neighbors[i].OccupiedBy is Cake)
                {
                    adjacentToCake = true;
                    break;
                }
            }
        }
        #endregion Find Cake

        #endregion Methods

        #region Draw
        public virtual void Draw(GameTime gameTime)
        {
            if (IsActive)
            {
                if (HasCake)
                {
                    base.Draw(gameTime, Var.FRAME_SPEED, 1);
                }
                else
                {
                    base.Draw(gameTime, Var.FRAME_SPEED, 0);
                }

                //if enemy needs effects displayed, draw them
                if (status_effects[1] || status_effects[2])
                {
                    if (status_effects[1])
                    {
                        effects.Draw(gameTime, Var.FRAME_SPEED, 0);
                    }
                    else if (status_effects[2])
                    {
                        effects.Draw(gameTime, Var.FRAME_SPEED, 1);
                    }
                }
                healthBar.Draw();
            }
        }
        #endregion Draw
    }
}